import React from 'react';
import type {
  CrudPageProps,
  CrudPageState,
  CrudPageConfig,
  Bean,
  ResultBean,
  BaseService,
} from '../interface';
import type {
  ActionType,
  ProColumns,
  ProTableProps,
  RequestData,
} from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { Button, message, Popconfirm } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import type { DetailFormRef } from './components/DetailForm';
import DetailForm from './components/DetailForm';
import OpActions from './components/OpActions';
import { genResultBeanData } from '../utils/transData/index';
// import type { FormSchema } from '@ant-design/pro-form/lib/components/SchemaForm';
import { asyncDelay } from '../utils/utils';

export default class CrudPage<
  P = CrudPageProps,
  S = CrudPageState,
> extends React.PureComponent<P & CrudPageProps, CrudPageState> {
  public crudPageConfig: CrudPageConfig = {} as CrudPageConfig;
  public refAddView: React.RefObject<DetailFormRef>;
  public refModifyView: React.RefObject<DetailFormRef>;
  // public refTableAction:
  //   | MutableRefObject<ActionType | undefined>
  //   | ((actionRef: ActionType) => void)
  //   | undefined;
  public refTableAction: React.RefObject<ActionType>;

  // public refTableAction: React.MutableRefObject<ActionType | undefined>;

  constructor(
    props: (P & CrudPageProps<Bean>) | Readonly<P & CrudPageProps<Bean>>,
  ) {
    super(props);
    this.refAddView = React.createRef();
    this.refModifyView = React.createRef();
    this.refTableAction = React.createRef();
    this.state = {
      selectedRecord: {},
    };
    this.setCurdPageConfig();
  }

  setCurdPageConfig() {
    console.error('TODO 等待覆盖');
  }

  getPageContainer() {
    const { crudPageConfig } = this;
    return crudPageConfig.PageContainer;
  }

  getAsyncService<T = ResultBean>(key: string) {
    const { crudPageConfig } = this;
    const { services } = crudPageConfig;
    if (typeof services[key] !== 'function') {
      throw new Error(`未定义服务${key}`);
    }
    console.info(`${key}服务被调用`);
    return services[key] as unknown as BaseService<T>;
  }

  calcParamsForAddOrModify(isModify: boolean, formData: Bean) {
    const { primaryKey } = this.crudPageConfig;
    if (!isModify) {
      return formData;
    }
    const { selectedRecord } = this.state;
    const params = {
      ...formData,
      [primaryKey]: selectedRecord[primaryKey],
    };
    return params;
  }

  async serviceQueryListPage(params: Bean) {
    const hide = message.loading('数据加载中...');
    try {
      const asyncService = this.getAsyncService('queryListPage');
      const results = await asyncService(params);
      return results;
    } catch (error) {
      if (error instanceof Error) {
        message.error(error.message || '查询失败');
      }
      console.log('throw error', error);
    } finally {
      hide();
    }
    return genResultBeanData<{ data: Bean[] }>(false);
  }

  async serviceRemove(item: Bean) {
    const { primaryKey } = this.crudPageConfig;
    const hide = message.loading('执行中...');
    try {
      const asyncService = this.getAsyncService('remove');
      const params = {
        [primaryKey]: item[primaryKey],
      };
      await asyncService(params);
      this.search();
    } catch (error) {
      if (error instanceof Error) {
        message.error(error.message || '操作失败');
      }
    } finally {
      hide();
    }
  }

  async serviceQueryDetail(item: Bean) {
    const { primaryKey } = this.crudPageConfig;
    const hide = message.loading('详情数据加载中...');
    try {
      const asyncService = this.getAsyncService('queryDetail');
      const params = {
        [primaryKey]: item[primaryKey],
      };
      const result = await asyncService(params);
      return result;
    } catch (error) {
      if (error instanceof Error) {
        message.error(error.message || '操作失败');
      }
      console.log('throw error', error);
    } finally {
      hide();
    }
    return genResultBeanData(false);
  }

  async serviceAddOrModify(
    isModify: boolean,
    formData: Bean,
    detailFormRef: DetailFormRef,
  ) {
    // const { primaryKey } = this.crudPageConfig;
    const hide = message.loading('保存载中...');
    try {
      const asyncService = this.getAsyncService(isModify ? 'modify' : 'add');
      const params = this.calcParamsForAddOrModify(isModify, formData);
      await asyncService(params);
    } catch (error) {
      if (error instanceof Error) {
        message.error(error.message || `${isModify ? '添加' : '编辑'}失败 ~`);
      }
      console.log('throw error', error);
    } finally {
      hide();
      // detailFormRef.triggerOpen();
    }
  }

  async search(reloadAndRest = false) {
    if (this.refTableAction.current) {
      const action = this.refTableAction.current;
      if (!reloadAndRest) {
        action.reload();
      } else {
        action.reset!();
      }
    }
  }

  getMoreOpActions(item?: Bean): JSX.Element[] {
    // return [
    //   <Button key="detail" size="small" type="link">
    //     查看
    //   </Button>,
    //   <Button key="detail1" size="small" type="link">
    //     查看1
    //   </Button>,
    //   <Button key="detail2" size="small" type="link">
    //     查看2
    //   </Button>,
    // ];
    return [];
  }
  getOpActions(item: Bean) {
    const actions = [
      <Button
        key="modify"
        size="small"
        type="link"
        onClick={() => {
          this.handleShowModifyView(item);
        }}
      >
        编辑
      </Button>,
      <Popconfirm
        key="remove"
        title="确认删除?"
        onConfirm={() => this.serviceRemove(item)}
        okText="删除"
        cancelText="取消"
      >
        <Button size="small" type="link">
          删除
        </Button>
      </Popconfirm>,
      ...this.getMoreOpActions(item),
    ];
    return actions;
  }

  getProTableOpColumn(): ProColumns<Bean> {
    const { columnPublicProps = {} } = this.crudPageConfig;
    return {
      title: '操作',
      key: 'op',
      fixed: 'right',
      ...columnPublicProps,
      hideInSearch: true,
      // @ts-ignore
      render: (text, record) => <OpActions list={this.getOpActions(record)} />,
    };
  }

  getProTableColumnProps(): ProColumns<Bean>[] {
    const { crudPageConfig } = this;
    const { columnPublicProps = {}, showOpColumn } = crudPageConfig;
    const columns = crudPageConfig.columns.map((item) => {
      return {
        ...item,
        ...columnPublicProps,
      };
    });
    if (!showOpColumn) {
      return columns;
    }
    return [...columns, this.getProTableOpColumn()];
  }

  getTableRequest = async (values: Bean) => {
    const { showConciseSearch, conciseSearchFieldKey } = this.crudPageConfig;
    const { keyword = '', ...params } = values;
    if (keyword && showConciseSearch) {
      params[conciseSearchFieldKey] = keyword;
    }
    console.info('🚀 ~ request: ~ values, params', values, params);

    const { data = [], pageInfo = { total: 0 } } =
      await this.serviceQueryListPage(params);
    const tableData: RequestData<Bean> = {
      data: data as Bean[],
      ...pageInfo,
    };
    return tableData;
  };

  getToolbarActions() {
    return [
      <Button
        key="tooAdd"
        icon={<PlusOutlined />}
        onClick={() => this.handleShowAddView()}
      >
        添加
      </Button>,
      // <Button icon={<EditOutlined />}>编辑</Button>,
    ];
  }

  getTableProps() {
    const { crudPageConfig } = this;
    const {
      primaryKey: recordPrimaryKey,
      tablePublicProps,
      columnPublicProps,
      paginationPublicProps,
      showConciseSearch,
      conciseSearchProps,
    } = crudPageConfig;
    const pageSizeOptions = ['10', '50', '100', '200'];
    const columns = this.getProTableColumnProps();
    const proTableProps: ProTableProps<Bean, Bean, 'text'> = {
      ...tablePublicProps,
      rowKey: recordPrimaryKey,
      columns,
      request: this.getTableRequest,
      pagination: {
        style: { textAlign: 'right' },
        showTotal: (total) => `总共 ${total} 条`,
        pageSizeOptions,
        size: 'default',
        ...paginationPublicProps,
      },
      // search: false,
      search: {
        defaultCollapsed: false,
      },
      options: {
        search: false,
      },
      toolbar: {
        actions: this.getToolbarActions(),
      },
    };
    proTableProps.scroll = {
      x: columns.length * Number(columnPublicProps.width || 190),
    };
    if (showConciseSearch) {
      proTableProps.search = false;
      if (proTableProps.options) {
        // @ts-ignore
        proTableProps.options.search = conciseSearchProps;
      }
    }
    return proTableProps;
  }

  getDetailFormProps(type: string) {
    const { detailFormLayout } = this.crudPageConfig;
    const { selectedRecord } = this.state;
    const formProps = {
      ...detailFormLayout,
      layoutType: 'DrawerForm',
    };
    return {
      formProps,
      selectedRecord,
    };
  }

  asyncSetState(data: Partial<CrudPageState>, callback?: () => void) {
    return new Promise<CrudPageState>((resolve) => {
      this.setState({ ...data }, () => {
        resolve(this.state);
        if (typeof callback === 'function') {
          callback();
        }
      });
    });
  }

  async handleShowAddView() {
    await this.asyncSetState({ selectedRecord: {} });
    this.refAddView.current?.triggerOpen();
  }

  async handleShowModifyView(item: Bean) {
    await this.asyncSetState({ selectedRecord: item });
    let resultData = {};
    const { loadDetailsForModify } = this.crudPageConfig;

    this.refModifyView.current?.triggerOpen();
    if (loadDetailsForModify) {
      const result = await this.serviceQueryDetail(item);
      resultData = result.data as Bean;
    }
    const values = loadDetailsForModify ? resultData : item;
    await asyncDelay(100);
    this.refModifyView.current?.formRef.current?.setFieldsValue(values);
  }

  handleDetailFormAddOrModify = async (
    formData: Bean,
    config: DetailFormRef,
  ) => {
    const { type } = config;
    if (type === 'detail') {
      return;
    }
    const isModify = type === 'modify';
    await this.serviceAddOrModify(isModify, formData, config);
    // config.triggerOpen();
    config.btnTriggerRef.current?.click();
    this.search();
    await asyncDelay(100);
    config.formRef.current?.resetFields();
  };

  rendTableView() {
    return (
      <ProTable
        {...this.getTableProps()}
        actionRef={
          this.refTableAction as React.MutableRefObject<ActionType | undefined>
        }
      />
    );
  }

  rendFormView(): JSX.Element[] {
    const { addFormColumns } = this.crudPageConfig;
    const addView = (
      <DetailForm
        type="add"
        key="add"
        hideTrigger={true}
        columns={addFormColumns}
        onFinish={this.handleDetailFormAddOrModify}
        ref={this.refAddView}
        {...this.getDetailFormProps('add')}
      />
    );
    const editView = (
      <DetailForm
        hideTrigger={true}
        type="modify"
        key="modify"
        columns={addFormColumns}
        onFinish={this.handleDetailFormAddOrModify}
        ref={this.refModifyView}
        {...this.getDetailFormProps('modify')}
      />
    );
    return [addView, editView];
  }
  rendMain() {
    return (
      <React.Fragment>
        {this.rendTableView()}
        <div className="block-spacing"></div>
        {this.rendFormView()}
      </React.Fragment>
    );
  }
  render() {
    const PageContainer = this.getPageContainer();
    return (
      <PageContainer title={<span></span>}>{this.rendMain()}</PageContainer>
    );
  }
}
